<?php
/**
 * @name Dispatch
 *
 * LICENSE
 *
 * This source file is subject to the BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.phpxe.com/license/
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to Chaodong.Sheng <scd113@gmail.com> so we can send you a copy immediately.
 *
 * @category   Controller of Scd Framework
 * @package    Scd
 * @copyright  Copyright (c) 2007 PHPXe Web Studio <http://www.phpxe.com>
 * @license    http://framework.phpxe.com/license/
 * @version    $Id: Abstract.php Tue Sep 25 23:08:01 CST 2007 Davy $
 */
include_once("Scd/Controller/Exception.php");
include_once("Scd/Controller/Route.php");
include_once("Scd/Filter/InputFilter.php");

class Scd_Controller_Dispath_Abstract {
	/**
     * Default action, Controller, Module
     * @var string
     */
	var $_defaultAction	= 'index';
	var $_defaultController = 'index';
	var $_defaultModule	= 'home';

	/**
     * Current module (formatted)
     * @var string
     */
	var $_curAction		= null;
	var $_curController	= null;
	var $_curModule		= null;

	/**
	 * Default Error
	 */
	var $_errAction		= 'index';
	var $_errController	= 'index';
	var $_errModule 	= 'error';

	/**
     * Array of invocation parameters to use when instantiating action
     * 初始化动作时的参数数组
     * controllers
     * @var array
     */
	var $_invokeParams	= array();
	//当前controller文件夹路径
	var $_curCtrlDir;
	//当前使用的动作方法名称
	var $_curActionName;
	//当前使用到的控制类名
	var $_curCtrlName;
	
	var $_dispatched	= false;
	var $_configIns		= null;
	var $_token			= null;
	
	/**引入过滤*/
	var $_filter		= null;

	function Scd_Controller_Dispath_Abstract() {
	}

	/**
	 * 获得config
	 *
	 * @param unknown_type $instance
	 */
	function _config( $instance )
	{
		if ( null === $this->_configIns && is_object($instance)) {
			$this->_configIns = $instance;
		}
		return $this;
	}
	
	/**
	 * Get Token from Route
	 */
	function getRequest()
	{
		$route = new Scd_Controller_Route();
		$this->_filter = new Scd_Filter_InputFilter();
		//$this->_token = $route->factory();
		$this->_token = $this->_filter->clean($route->factory());
		foreach ($this->_token as $key => $value) {
			if ($value !== null) {
				$params[$key] = $value;
			} elseif (isset($params[$key])) {
				unset($params[$key]);
			}
		}
		unset($params[$route->_moduleKey]);
		unset($params[$route->_controllerKey]);
		unset($params[$route->_actionKey]);
		$this->_invokeParams	= &$params;
		return $this;
	}

	/**
     * 获得module name
     * 判断_token中是否存在module,不存在或错误的使用_defaultModule
     *
     * @return string $this->_curModule
     */
	function getModuleName()
	{
		if (null <> $this->_token && isset($this->_token['module']) && $this->_token['module'] && $this->isValidModule($this->_token['module'])) {
			$this->_curModule = $this->_token['module'];
		}else {
			$this->_curModule = $this->_defaultModule;
		}
		return $this->_curModule;
	}

	/**
	 * 模块是否存在
	 *
	 * @param 模块名称 $moduleName
	 * @return boolean
	 */
	function isValidModule( $moduleName )
	{
		if (null <> $moduleName) {
			return file_exists($this->getControllerDirectory($moduleName));
		}
		return false;
	}

	/**
	 * 获得默认控制类
	 * 并定义当前控制类文件夹路径
	 *
	 */
	function getDefaultControllerClass()
	{
		$this->_curController	= $this->formatControllerName($this->_defaultController);
		$this->_curCtrlDir		= $this->getControllerDirectory($this->getModuleName());
		return $this->_curController;
	}

	/**
	 * 获得动作方法
	 *
	 * @return unknown
	 */
	function getActionMethod()
	{
		$this->_curAction		= isset($this->_token['action']) && $this->_token['action'] ? $this->_token['action'] : $this->_defaultAction;
		//如果转向错误
		if ( 'error' == $this->_curModule ){
			$this->_curAction	= $this->_errAction;
		}
		$this->_curActionName	= $this->formatActionName($this->_curAction);
		return $this->_curActionName;
	}

	/**
	 * 得到控制类名字如indexController
	 */
	function getControllerClass()
	{
		//当前控制目录
		$this->_curCtrlDir		= $this->getControllerDirectory($this->getModuleName());
		$this->_curController	= isset($this->_token['controller'])&&$this->_token['controller'] ? $this->_token['controller'] : $this->_defaultController;
		$this->_curCtrlName		= $this->formatControllerName($this->_curController);
		/**
		 * 如果不存在controllerClass
		 * 意味着找不到相关controller的文件输出错误；
		 * 首先考虑到直接默认index，后发现这样并不能连接至静态页面作罢
		 */
		if (file_exists($this->_curCtrlDir. DIRECTORY_SEPARATOR .$this->_curCtrlName.".php")) {
			return $this->_curCtrlName;
		}else {
			//返回index
			//return $this->getDefaultControllerClass();
			//或者返回controller问题错误
			//激发$this->_curCtrlName为errcontroller
			$this->_invokeParams['msg'] = $this->_token['controller'];
			$this->_errAction	= 'ctr';
			return $this->getErrorControllerClass();
		}
	}

	/**
	 * 制定转向错误Controller
	 */
	function getErrorControllerClass()
	{
		$this->_curModule		= $this->_errModule;
		$this->_curCtrlDir		= $this->getControllerDirectory($this->_curModule);
		return $this->_curCtrlName = $this->formatControllerName($this->_errController);
	}

	/**
     * Return the currently set directories for Scd_Controller_Action class lookup
     *
     * If a module is specified, returns just that directory.
     * 
     * @param  string $module Module name
     * @return array|string Returns array of all directories by default, single 
     * module directory if module argument provided
     */
	function getControllerDirectory($module = null)
	{
		$directories			= $this->_configIns->app_path.DIRECTORY_SEPARATOR.$module .DIRECTORY_SEPARATOR. $this->_configIns->controller_path;
		return $directories;
	}

	function isDispatchable()
	{
		if (false == $this->_dispatched) {
			$this->_dispatched		= isset($this->_token)&&$this->_token;
		}
		return $this->_dispatched;
	}

	/**
	 * 手动设置
	 *
	 * @param boolean $bol
	 */
	function setDispatched( $bol )
	{
		$this->_dispatched		= $bol;
	}

	/**
     * Return the registered response object
     * 准备做反馈的
     * @return Scd_Controller_Response_Abstract|null
     */
	function getResponse()
	{
	}

	/**
     * Retrieve action controller instantiation parameters
     * @todo $this->getRequest()
     * @return array
     */
	function getParams()
	{
		return $this->_invokeParams;
	}

	/**
     * Dispatch to a controller/action
     *
     * @return boolean
     */
	function dispatch()
	{
		//取得Token值
		$request = $this->getRequest();
		//获得
		$this->getControllerClass();
		/**
         * Retrieve the action name
         * 重获action 名称
         */
		$this->getActionMethod();
		
		$this->setDispatched(true);
		/**
		 * 分配所有参数后_token, $_configIns不需要了
		 */
		$this->destory();
	}

	/**
     * Formats a string from a URI into a PHP-friendly name.
     *
     * By default, replaces words separated by the word separator character(s)
     * with camelCaps. If $isAction is false, it also preserves replaces words
     * separated by the path separation character with an underscore, making
     * the following word Title cased. All non-alphanumeric characters are
     * removed.
     *
     * @param string $unformatted
     * @param boolean $isAction Defaults to false
     * @return string
     */
	function _formatName($unformatted)
	{
		$unformatted = preg_replace('/[^a-z0-9 ]/', '', $unformatted);
		$unformatted = str_replace(' ', '', ucwords($unformatted));
		return $unformatted;
	}

	/**
	 * Formats a string into a module name.
	 */
	function formatModuleName($unformatted)
	{
		if ($this->_defaultModule == $unformatted) {
			return $unformatted;
		}
		return $unformatted;
	}

	/**
     * Formats a string into a controller name.  This is used to take a raw
     * controller name, such as one stored inside a Scd_Controller_Request_Abstract
     * object, and reformat it to a proper class name that a class extending
     * Scd_Controller_Action would use.
     *
     * @param string $unformatted
     * @return string
     */
	function formatControllerName($unformatted)
	{
		return ucfirst($this->_formatName($unformatted)) . 'Controller';
	}

	/**
     * Formats a string into an action name.  This is used to take a raw
     * action name, such as one that would be stored inside a Scd_Controller_Request_Abstract
     * object, and reformat into a proper method name that would be found
     * inside a class extending Scd_Controller_Action.
     *
     * @param string $unformatted
     * @return string
     */
	function formatActionName($unformatted)
	{
		$formatted = $this->_formatName($unformatted, true);
		return strtolower(substr($formatted, 0, 1)) . substr($formatted, 1) . 'Action';
	}

	/**
	 * load应用类
	 *
	 * @param 类名 $className
	 * @return include
	 */
	function loadClass( $className )
	{
		//dump($this);exit;
		$loadFile    = $this->_curCtrlDir . DIRECTORY_SEPARATOR . $this->classToFilename($className);
		$loadFile	 = str_replace("\\", "/", $loadFile);
		$dir         = dirname($loadFile);
		$file        = basename($loadFile);

		if (Scd::loadFile($file, $dir) == false) {
			return new Scd_Controller_Exception("#__Can`t load this file!");
		}
		return $className = ucfirst($this->formatModuleName($className));
	}

	/**
     * Convert a class name to a filename
     *
     * @param string $class
     * @return string
     */
	function classToFilename($class)
	{
		return str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
	}
	
	/**
	 * 销毁_configIns；
	 * 防止问题出现。
	 */
	function destory()
	{
		$this->_configIns	= null;
		$this->_token		= null;
	}
}